//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include "CAudioCard.h"
#include <CAudioIC.h>
#include <zenr_api.h>
#include "keypad.h"
#include "_sprintf.h"

enum VibratorStatus {
    VibratorStatus_Enable   = 0x01,
    VibratorStatus_Disable,
};

enum TouchVibrationLevel {
    Vibrate_Disable,
    Vibrate_Weak,
    Vibrate_Normal,
    Vibrate_Strong,
};

#define NU_VK_LINECONTROL                0x88

static DzEvent  s_vibratorEvent;
static Millisecond32   s_msVibratorOn  = 1500;
static Millisecond32   s_msVibratorOff = 1000;
static Int32 s_touchVirationlevel = Vibrate_Normal;
extern Bool32 g_allDevicesDown;
Int32 g_playThenAllDevicesDown = 0;
EXTERN void PoSetAudioPowerOn(Boolean PowerOn);

Boolean _IsAudioBusy()
{
    //cprintf("Current audio state %x\n", s_audio.m_audioCxt.state);
    return FALSE;
}

void _AudioLock()
{
    return;
}

void _AudioUnlock()
{
    return;
}

static Int32 VibratorControlThread(void * pArg)
{
    uint_t uWaitTicks = UINT_MAX;
    WaitResult wr;
    EventState state;
    bool_t bVibratorOn = FALSE;

    uint_t vibratorOnTicks = DzMillisecondsToTicks(s_msVibratorOn);
    uint_t vibratorOffTicks = DzMillisecondsToTicks(s_msVibratorOff);

    while (1) {
        if (UINT_MAX == uWaitTicks) {
            s_vibratorEvent.Wait(&wr, &state);
        }
        else {
            s_vibratorEvent.TryWait(uWaitTicks, &wr, &state);
        }

        if (WaitResult_TimedOut == wr) {
            bVibratorOn ^= TRUE;
            uWaitTicks = bVibratorOn ? vibratorOnTicks : vibratorOffTicks;
            nu_EnableVibrator(NULL, bVibratorOn);
            continue;
        }

        if (VibratorStatus_Enable == state) {
            uWaitTicks = vibratorOnTicks;
            bVibratorOn = TRUE;
        }
        else if (VibratorStatus_Disable == state) {
            uWaitTicks = UINT_MAX;
            bVibratorOn = FALSE;
        }
        else {
            continue;
        }

        nu_EnableVibrator(NULL, bVibratorOn);
    }

    assert(0 && "unreachable here");
    return 0;
}

static CAudioCard s_audio;

static AudioPCMBuffer s_AudioBuffer;
extern CKeypad * g_pKeypad;

void audio_event_callback(unsigned char session_number,
                        unsigned char eventId)
{
    switch(eventId)
    {
        case AUDIO_APPS_EVENT_SYNC_UPDATE_IND: {
            nu_notifyDeviceCaller(
                (Int32)&(s_audio.m_AudioCallbackEvent), 0x101);
            break;
        }

        case AUDIO_APPS_EVENT_OVERFLOW_IND: {
            nu_puts("*****overflow\n");
            break;
        }

        case AUDIO_APPS_EVENT_UNDERFLOW_IND: {
            nu_puts("*****underflow\n");
            break;
        }

        case AUDIO_APPS_EVENT_STOP_ERROR: {
            nu_puts("*****stop error\n");
            break;
        }

        case AUDIO_APPS_EVENT_SESSION_STOPPED: {
            nu_puts("*****session stopped\n");
            nu_notifyDeviceCaller(
                (Int32)&(s_audio.m_AudioStopEvent), 0x102);
            break;
        }

        case AUDIO_APPS_EVENT_START_ERROR: {
            nu_puts("*****start error\n");
            break;
        }

        case AUDIO_APPS_EVENT_UPDATE_ERROR: {
            nu_puts("*****updatet error\n");
            break;
        }
    }

}

int write_samples(short *samples, unsigned long size, unsigned long *copysize)
{
    unsigned char audio_session_number =
        (unsigned char)s_AudioBuffer.sessionNumber;
    unsigned long read_offset =
        zapi_Audio->GetASFStatus(audio_session_number);
    int res = BUFFER_SUCCESS;

    if(read_offset < s_AudioBuffer.writeOffset) {

        if(size <= s_AudioBuffer.bufferSize -
            s_AudioBuffer.writeOffset + read_offset - 1) {

            *copysize = size;

            if(size >= s_AudioBuffer.bufferSize - s_AudioBuffer.writeOffset) {
                memcpy(
                    (short *)(s_AudioBuffer.bufferStart +
                        s_AudioBuffer.writeOffset),
                    samples,
                    (s_AudioBuffer.bufferSize - s_AudioBuffer.writeOffset)
                        * sizeof(short));

                memcpy(
                    s_AudioBuffer.bufferStart,
                    samples + s_AudioBuffer.bufferSize -
                        s_AudioBuffer.writeOffset ,
                    ((unsigned long)(size -
                        (s_AudioBuffer.bufferSize -
                        s_AudioBuffer.writeOffset))) * sizeof(short));

                s_AudioBuffer.writeOffset = size -
                    (s_AudioBuffer.bufferSize - s_AudioBuffer.writeOffset);
            }
            else {
                memcpy(
                    (short *)(s_AudioBuffer.bufferStart +
                        s_AudioBuffer.writeOffset),
                    samples,
                    size * sizeof(short));

                s_AudioBuffer.writeOffset += size;
            }
        }
        else {
            *copysize = s_AudioBuffer.bufferSize - s_AudioBuffer.writeOffset
                + read_offset - 1;

            if (!read_offset) {

                memcpy(
                    (short *)(s_AudioBuffer.bufferStart +
                        s_AudioBuffer.writeOffset),
                    samples,
                    (s_AudioBuffer.bufferSize -
                        s_AudioBuffer.writeOffset - 1) * sizeof(short));

                s_AudioBuffer.writeOffset = s_AudioBuffer.bufferSize - 1;

                res = BUFFER_FULL;
            }
            else {

                memcpy(
                    (short *)(s_AudioBuffer.bufferStart +
                        s_AudioBuffer.writeOffset),
                    samples,
                    (s_AudioBuffer.bufferSize -
                        s_AudioBuffer.writeOffset) * sizeof(short));

                memcpy(
                    s_AudioBuffer.bufferStart,
                    samples + s_AudioBuffer.bufferSize -
                        s_AudioBuffer.writeOffset,
                    (read_offset - 1) * sizeof(short));

                s_AudioBuffer.writeOffset = read_offset - 1;

                res = BUFFER_FULL;
            }
        }
    }
    else {
        if (read_offset - s_AudioBuffer.writeOffset < (UInt32)s_audio.m_BufferSize / 8
            && s_AudioBuffer.isStarted != 0
            && read_offset != s_AudioBuffer.writeOffset) {
            return BUFFER_SUSPEND;
        }
        if((size <= read_offset - s_AudioBuffer.writeOffset - 1) ||
            (read_offset == s_AudioBuffer.writeOffset &&
            size < s_AudioBuffer.bufferSize)) {
            *copysize = size;

            memcpy((Int16 *)(
                s_AudioBuffer.bufferStart + s_AudioBuffer.writeOffset),
                samples,
                size * sizeof(short));

            s_AudioBuffer.writeOffset += size;
        }
        else {
            *copysize = read_offset - s_AudioBuffer.writeOffset - 1;

            memcpy(
                (short *)(s_AudioBuffer.bufferStart +
                    s_AudioBuffer.writeOffset),
                samples,
                (*copysize) * sizeof(short));

            s_AudioBuffer.writeOffset += *copysize;

            res = BUFFER_FULL;
        }
    }
    if (s_AudioBuffer.writeOffset == s_AudioBuffer.bufferSize) {
        s_AudioBuffer.writeOffset = 0;
    }
    return res;
}

ECode CAudioCard::Audio_Write(short *wav_buffer, unsigned long size, unsigned long *pCopy_Size)
{
    int eError = 0;
    ECode ec = NOERROR;
    unsigned long copysize = 0;
//    unsigned long remainsize = 0;
//    short *copy_buffer = wav_buffer;
//    WaitResult wr;
//    EventState nState;

    if (0 == s_AudioBuffer.isStarted) {
        eError = write_samples(wav_buffer, size, &copysize);
        if (BUFFER_SUCCESS == eError) {
            zapi_Audio->SetAppStatus(
                s_AudioBuffer.sessionNumber, s_AudioBuffer.writeOffset);
            *pCopy_Size = size * 2;
        }
        else if (eError == BUFFER_FULL) {
            zapi_Audio->SetAppStatus(
                s_AudioBuffer.sessionNumber, s_AudioBuffer.writeOffset);

            zapi_Audio->StartSession(s_AudioBuffer.sessionNumber,
                s_AudioBuffer.wakeupEventPeriod, 0, 0x1000, 0, 0x7fff);

            s_AudioBuffer.isStarted = 1;
            s_AudioBuffer.isStopped = 0;
            *pCopy_Size = copysize * 2;

//            remainsize = size - copysize;
//            do {
//                m_AudioCallbackEvent.Wait(&wr, &nState);
//                copy_buffer += copysize;
//                eError = write_samples(copy_buffer, remainsize, &copysize);
//                nu_Audio_SetAppStatus(
//                    s_AudioBuffer.sessionNumber, s_AudioBuffer.writeOffset);
//                remainsize -= copysize;
//            } while (remainsize > 0);

        }
        else {
            *pCopy_Size = 0;
            ec = E_FAIL;
        }
    }
    else {
        eError = write_samples(wav_buffer, size, &copysize);
        if (BUFFER_SUCCESS == eError) {
            zapi_Audio->SetAppStatus(
                s_AudioBuffer.sessionNumber, s_AudioBuffer.writeOffset);
            *pCopy_Size = size * 2;
        }
        else if (BUFFER_FULL == eError) {
            zapi_Audio->SetAppStatus(
                s_AudioBuffer.sessionNumber, s_AudioBuffer.writeOffset);

            *pCopy_Size = copysize * 2;

//            remainsize = size - copysize;
//            do {
//                m_AudioCallbackEvent.Wait(&wr, &nState);
//                copy_buffer += copysize;
//                eError = write_samples(copy_buffer, remainsize, &copysize);
//                nu_Audio_SetAppStatus(
//                    s_AudioBuffer.sessionNumber, s_AudioBuffer.writeOffset);
//                remainsize -= copysize;
//            } while (remainsize > 0);

        }
        else if (BUFFER_SUSPEND == eError) {
            *pCopy_Size = 0;
        }
        else {
            *pCopy_Size = 0;
            ec = E_FAIL;
        }
    }
    return ec;
}

static RecordPCMBuffer s_RecorderBuffer;

ECode read_samples(Uint8* data, Uint32 uBytesToRead, Uint32* numOfBytesRead)
{
    Int8* secondaryStreamPtr;
    Uint16 numOfBytesCopied;
    Uint16 i;
    Uint16 numOfBytesToCopy;
    Uint16 numOfBytesToEndOfBuf;
    Uint16 status = 0;
    Audio_Result audioResult;

    s_RecorderBuffer.DSPStatus =
        zapi_Audio->GetASFStatus(s_RecorderBuffer.sessionNumber);

    // Check if the App Status(read pointer) is less than
    // the DSP status(write pointer)
    if (s_RecorderBuffer.AppStatus < s_RecorderBuffer.DSPStatus) {
        // Set the pointer to point to the current read pointer
        // and set the number of bytes to copy
        if (s_RecorderBuffer.DSPStatus - s_RecorderBuffer.AppStatus < uBytesToRead) {
            *numOfBytesRead = 0;
            return BUFFER_SUSPEND;
        }
        if (s_RecorderBuffer.unitByte == TRUE) {
            secondaryStreamPtr = (Int8 *)(s_RecorderBuffer.bufferStart
                       + s_RecorderBuffer.AppStatus);
            numOfBytesToCopy =
                s_RecorderBuffer.DSPStatus - s_RecorderBuffer.AppStatus;
        }
        else {
            secondaryStreamPtr = (Int8 *)(s_RecorderBuffer.bufferStart
                                + s_RecorderBuffer.AppStatus);
            numOfBytesToCopy =
                2 * (s_RecorderBuffer.DSPStatus - s_RecorderBuffer.AppStatus);
        }

        // Calculate the number of Bytes read and the new App status
        if (numOfBytesToCopy > uBytesToRead) {
            numOfBytesToCopy = uBytesToRead;
            *numOfBytesRead = uBytesToRead;
            if (s_RecorderBuffer.unitByte == TRUE)
                status = s_RecorderBuffer.AppStatus + uBytesToRead;
            else
                status = s_RecorderBuffer.AppStatus + uBytesToRead / 2;
        }
        else {
            *numOfBytesRead = numOfBytesToCopy;
            status = s_RecorderBuffer.DSPStatus;
        }

        // copy the bytes into the array
        if (s_RecorderBuffer.unitByte == TRUE) {
            for (i = 0; i < numOfBytesToCopy; i++ ) {
                *data++ = (Uint8)(*secondaryStreamPtr++);
            }
        }
        else {
            for (i = 0; i < numOfBytesToCopy / 2; i++ ) {
                /* for LSB MSB */
                data[2*i]   = (Uint8)secondaryStreamPtr[2*i];
                data[2*i+1] = (Uint8)secondaryStreamPtr[2*i+1];
            }
        }

    }
    // Check if the App status is greater than the DSP status
    else if (s_RecorderBuffer.AppStatus > s_RecorderBuffer.DSPStatus) {
        // Set the pointer to the current read pointer
        // and calculate the number of bytes to the end of the buffer
        if (s_RecorderBuffer.unitByte == TRUE) {
            secondaryStreamPtr = (Int8 *)(s_RecorderBuffer.bufferStart
                        + s_RecorderBuffer.AppStatus);
            numOfBytesToEndOfBuf =
                s_RecorderBuffer.bufferSize - s_RecorderBuffer.AppStatus;
        }
        else {
            secondaryStreamPtr = (Int8 *)(s_RecorderBuffer.bufferStart
                                + s_RecorderBuffer.AppStatus);
            numOfBytesToEndOfBuf =
                2 * (s_RecorderBuffer.bufferSize - s_RecorderBuffer.AppStatus);
        }

        if (s_RecorderBuffer.DSPStatus + numOfBytesToEndOfBuf < uBytesToRead) {
            *numOfBytesRead = 0;
            return BUFFER_SUSPEND;
        }

        if (numOfBytesToEndOfBuf >= uBytesToRead) {

            // Copy uBytesToRead bytes from the App status
            if (s_RecorderBuffer.unitByte == TRUE)
            {
                for (i = 0; i < uBytesToRead; i++ ) {
                    *data++ = (Uint8)(*secondaryStreamPtr++);
                }
            }
            else{
                for (i = 0; i < uBytesToRead/2; i++ ) {
                    // for LSB MSB
                    data[2*i]   = (Uint8)secondaryStreamPtr[2*i];
                    data[2*i+1] = (Uint8)secondaryStreamPtr[2*i+1];
                }
            }

            // Calculate the number of Bytes read and the new App status
            numOfBytesToCopy = uBytesToRead;
            *numOfBytesRead = uBytesToRead;
            if (s_RecorderBuffer.unitByte == TRUE)
                status = s_RecorderBuffer.AppStatus + uBytesToRead;
            else
                status = s_RecorderBuffer.AppStatus + uBytesToRead/2;
        }
        else if (numOfBytesToEndOfBuf < uBytesToRead &&
            numOfBytesToEndOfBuf + s_RecorderBuffer.DSPStatus >= uBytesToRead) {

            // Copy the bytes from the App status to the buffer ending
            if (s_RecorderBuffer.unitByte == TRUE) {
                for(i = 0; i < numOfBytesToEndOfBuf; i++) {
                    *data++ = (Uint8)(*secondaryStreamPtr++);
                }
            }
            else{
                for(i = 0; i < numOfBytesToEndOfBuf/2; i++) {
                    /* for LSB MSB */
                    data[2*i]   = (Uint8)secondaryStreamPtr[2*i];
                    data[2*i+1] = (Uint8)secondaryStreamPtr[2*i+1];
                }
            }

            // Words copied till now
            numOfBytesCopied = numOfBytesToEndOfBuf;

            // Set the pointer to start of the buffer
            secondaryStreamPtr = (Int8 *)s_RecorderBuffer.bufferStart;

            // Copy remain bytes from the buffer start
            numOfBytesToCopy = uBytesToRead - numOfBytesToEndOfBuf;
            if (s_RecorderBuffer.unitByte == TRUE) {
                for (i=0; i < numOfBytesToCopy; i++ ) {
                    *data++ = (Uint8)(*secondaryStreamPtr++);
                }
            }
            else {
                for (i = numOfBytesCopied / 2;
                    i < (numOfBytesCopied + numOfBytesToCopy) / 2; i++ ) {
                    /* for LSB MSB */
                    data[2*i]   =
                        (Uint8)secondaryStreamPtr[2*(i-numOfBytesCopied/2)];
                    data[2*i+1] =
                        (Uint8)secondaryStreamPtr[2*(i-numOfBytesCopied/2)+1];
                }
            }

            // Calculate the number of Bytes read and the new App status
            *numOfBytesRead = uBytesToRead;
            if (s_RecorderBuffer.unitByte == TRUE)
                status = numOfBytesToCopy;
            else
                status = numOfBytesToCopy/2;
        }
        else if (numOfBytesToEndOfBuf + s_RecorderBuffer.DSPStatus
                < uBytesToRead) {

            // Copy the bytes from the App status to the buffer ending
            if (s_RecorderBuffer.unitByte == TRUE)
            {
                for (i=0; i < numOfBytesToEndOfBuf; i++ ) {
                    *data++ = (Uint8)(*secondaryStreamPtr++);
                }
            }
            else{
                for(i=0; i < numOfBytesToEndOfBuf/2; i++ ) {
                    // for LSB MSB
                    data[2*i]   = (Uint8)secondaryStreamPtr[2*i];
                    data[2*i+1] = (Uint8)secondaryStreamPtr[2*i+1];
                }
            }

            // Words copied till now
            numOfBytesCopied = numOfBytesToEndOfBuf;

            // Set the pointer to start of the buffer
            secondaryStreamPtr = (Int8 *)s_RecorderBuffer.bufferStart;

            // Copy the bytes from the buffer start to the buffer ending
            //numOfBytesToCopy = s_RecorderBuffer.DSPStatus;
            if (s_RecorderBuffer.unitByte == TRUE) {
                // Copy the bytes from the buffer start to the buffer ending
                numOfBytesToCopy = s_RecorderBuffer.DSPStatus;
                for (i=0; i < numOfBytesToCopy; i++ ) {
                    *data++ = (Uint8)(*secondaryStreamPtr++);
                }
            }
            else {
                // Copy the bytes from the buffer start to the buffer ending
                numOfBytesToCopy = s_RecorderBuffer.DSPStatus * 2;
                for (i = numOfBytesCopied / 2;
                    i < (numOfBytesCopied+numOfBytesToCopy) / 2; i++ ) {
                    // for LSB MSB
                    data[2*i]   =
                        (Uint8)secondaryStreamPtr[2*(i-numOfBytesCopied/2)];
                    data[2*i+1] =
                        (Uint8)secondaryStreamPtr[2*(i-numOfBytesCopied/2)+1];
                }
            }

            // Calculate the number of Bytes read and the new App status
            *numOfBytesRead = numOfBytesCopied + numOfBytesToCopy;
            status = s_RecorderBuffer.DSPStatus;
        }
    }
    else
    {
        *numOfBytesRead = 0;
        // Call the Set App status function so that the function will reset
        // the internal variables if required
        zapi_Audio->SetAppStatus(s_RecorderBuffer.sessionNumber,
                    s_RecorderBuffer.AppStatus );
        return BUFFER_EMPTY;
    }

    // If the adjusted status reached the buffer size, roll it back to 0
    if (status == s_RecorderBuffer.bufferSize)
    {
        status = 0;
    }
    audioResult = (Audio_Result)zapi_Audio->SetAppStatus(
                    s_RecorderBuffer.sessionNumber, status );

    // Update the App status
    s_RecorderBuffer.AppStatus = status;

    if (s_RecorderBuffer.unitByte == FALSE) {
        *numOfBytesRead /= 2;
    }

    return BUFFER_SUCCESS;
}

void DumpRecordData(Uint8 *pData, Uint32 nSize)
{
    Uint8 *p = pData;
    Uint32 n = nSize;
    while (n > 0) {
        for (int i = 0; i < 16; i++) {
            kprintf("%02x ", *p);
            n--;
            if (n == 0) {
                break;
            }
            p++;
        }
        kprintf("\n");
    }
    return;
}

ECode CAudioCard::Audio_Read(UInt8* pBuffer, Int32 uBytesToRead, Int32 *pBytesRead)
{
//    Int32 uReadSize = 0, uNeedToRead = 0;
//    UInt8 *pRecordData = (UInt8 *)pBuffer;
    Int32 res = 0;
//    WaitResult wr;

//    m_AudioCallbackEvent.Wait(&wr, NULL);

//    memset(pBuffer, 0, uBytesToRead * sizeof(UInt8));

    if (s_RecorderBuffer.unitByte == TRUE) {
        res = read_samples((Uint8 *)pBuffer, uBytesToRead,
                        (Uint32 *)pBytesRead);
    }
    else {
        res = read_samples((Uint8 *)pBuffer, uBytesToRead * 2,
                        (Uint32 *)pBytesRead);
    }

//    kprintf("after read samples: res=%d, uReadSize=%d\n", res, uReadSize);
//    kprintf("************************************\n");
//    DumpRecordData((Uint8 *)pBuffer, uReadSize);
//    kprintf("************************************\n");
//    if (uReadSize < uBytesToRead) {
//        uNeedToRead = uBytesToRead - uReadSize;
//       kprintf("reord begin loop: uNeedToRead=%d\n", uNeedToRead);
//        do {
//            m_AudioCallbackEvent.Wait(&wr, NULL);
//            pRecordData += uReadSize;
//            if (s_RecorderBuffer.unitByte == TRUE) {
//                res = read_samples((Uint8 *)pRecordData, uNeedToRead,
//                                (Uint32 *)(&uReadSize));
//            }
//            else {
//                res = read_samples((Uint8 *)pRecordData, uNeedToRead * 2,
//                                (Uint32 *)(&uReadSize));
//            }
//            kprintf("in while loop after read samples: res=%d,
//                    uReadSize=%d\n", res, uReadSize);
//            kprintf("************************************\n");
//            DumpRecordData((Uint8 *)pBuffer, uReadSize);
//            kprintf("************************************\n");
//            uNeedToRead -= uReadSize;
//            kprintf("in while loop uNeedToRead=%d\n", uNeedToRead);
//        }while(uNeedToRead > 0);
//    }

    return NOERROR;
}

//#define RECORD_IN_ELASTOS

ECode CAudioCard::Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent ** ppCompletionEvent)
{
    if (pBuffer == NULL) {
        return E_INVALID_ARGUMENT;
    }
    Int32 ReadSize = 0;
    void *pBuf = pBuffer->GetPayload();
    ECode ec = NOERROR;
    WaitResult wr;

    //DBGOUT(DBG_READ, ("CAudioCard::Read, pBuffer: 0x%x, uSizeRead: 0x%x\n",
    //    pBuffer, bytesToRead));

    m_lock.Lock(&wr);

#ifdef RECORD_IN_ELASTOS
    ec = Audio_Read((UInt8 *)pBuf, bytesToRead, &ReadSize);
#else
    int res;
    res =zapi_Audio->Recorder_Read((char *)pBuf,
                (unsigned long)bytesToRead, (unsigned long *)&ReadSize);
    if (res == BUFFER_ERROR) ec = E_FAIL;
#endif
    pBuffer->SetUsed(ReadSize);

    m_lock.Unlock();
    return ec;
}

static short s_buffer16[256];

void initbuffer16()
{
    //8bit  pcm: unsigned char     0-255
    //16bit pcm: short            -32767-32767
    for (int i = 0; i < 256; i++) {
        s_buffer16[i] = i * 65535 / 255 - 32767;
    }
    return;
}

short *convert8to16(unsigned char *buffer8, unsigned long bufsize)
{
    short *buffer16 = (short *)malloc(bufsize * sizeof(short));
    if (!buffer16) return NULL;

    for (int i = 0; i < (int)bufsize; i++) {
        buffer16[i] = s_buffer16[buffer8[i]];
    }
    return buffer16;
}

ECode CAudioCard::Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf &buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent ** ppCompletionEvent)
{
    void *pBuffer = buffer.GetPayload();
    unsigned long size = buffer.GetUsed();
    ECode ec = NOERROR;
    WaitResult wr;

    if (!pBytesWritten) return E_INVALID_ARGUMENT;
    //DBGOUT(DBG_WRITE, ("CAudioCard::Write, pBuffer: 0x%x, uSizeWrite: 0x%x\n",
    //    pBuffer, uSizeWrite));

    m_lock.Lock(&wr);

    if (m_SampleRate == 8000 && m_Channels == 1) {
        if (s_AudioBuffer.bufferStart == 0) {
            //audio has already been closed
            m_lock.Unlock();
            return E_ACCESS_DENIED;
        }
        short *buffer16 = NULL;
        if (m_BitRate == 8) {
            unsigned long copysize;
            buffer16 = convert8to16((unsigned char *)pBuffer, size);
            if (!buffer16) {
                m_lock.Unlock();
                return E_OUT_OF_MEMORY;
            }
            ec= Audio_Write(buffer16, size, &copysize);
            *pBytesWritten = copysize / 2;
            free(buffer16);
        }
        else {
            ec= Audio_Write((short *)pBuffer, size / 2,
                    (unsigned long *)pBytesWritten);
        }
    }
    else {
//        if (m_Channels == 1) {
//            //mono to stereo
//            kprintf("mono to stereo size:%d\n", size);
//            Byte *pSrc = (Byte *)pBuffer;
//            Byte *pDst = (Byte *)malloc(size * 2);
//            for (UInt32 i=0; i < size; i++) {
//                memcpy(pDst, pSrc, m_BitRate / 8);
//                pDst += m_BitRate / 8;
//                memcpy(pDst, pSrc, m_BitRate / 8);
//                pDst += m_BitRate / 8;
//                pSrc += m_BitRate / 8;
//            }
//            unsigned long copy_size = 0;
//            ec = (ECode)audio_write((short*)pDst, size, &copy_size);
//            if (ec != 0) {
//                ec = E_FAIL;
//            }
//
//            *pBytesWritten = copy_size / 2;
//
//            m_lock.Unlock();
//            return ec;
//        }
        short *buffer16 = NULL;
        if (m_BitRate == 8) {
            unsigned long copysize;
            buffer16 = convert8to16((unsigned char *)pBuffer, size);
            if (!buffer16) {
                m_lock.Unlock();
                return E_OUT_OF_MEMORY;
            }
            ec= zapi_Audio->Write48k(buffer16,
                                     size,
                                     &copysize);

            *pBytesWritten = copysize / 2;
            free(buffer16);
        }
        else {
            ec = zapi_Audio->Write48k((short*)pBuffer,
                                 size / 2,
                                 (unsigned long *)pBytesWritten);
        }
        if (ec == -2) {
            ec = E_ACCESS_DENIED;
        }
        else if (ec != 0) {
            ec = E_FAIL;
        }
    }

    m_lock.Unlock();
    return ec;
}

ECode CAudioCard::Control(
                /* [in] */ Handle32 nControlCode,
                /* [in] */ const MemoryBuf &inBuffer,
                /* [out] */ MemoryBuf * pOutBuffer,
                /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec = NOERROR;
    Byte *pInData;
    int inDataSize;
    WaitResult wr;

    inDataSize = inBuffer.IsNullOrEmpty() ? 0 : inBuffer.GetUsed();
    pInData = inBuffer.IsNullOrEmpty() ? NULL : inBuffer.GetPayload();

    m_lock.Lock(&wr);

    switch (nControlCode) {
        ////////////////////////////////////////////////////////////////////////////
        //
        // Audiocard State 1: STATE_AUDIOOUT
        //
        case 0x00: {
            if (inDataSize < (int)sizeof(Audio_Params)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            if (g_allDevicesDown) {
                g_playThenAllDevicesDown = 1;
            }
            m_SampleRate = ((AudioParamsWithBufSize *)pInData)->params.SampleRate;
            m_BitRate = ((AudioParamsWithBufSize *)pInData)->params.BitsPerSample;
            m_Channels = ((AudioParamsWithBufSize *)pInData)->params.Channels;

            if (inDataSize == sizeof(Audio_Params) + sizeof(int)) {
                m_BufferSize = ((AudioParamsWithBufSize *)pInData)->buf_size;
            }
            else {
                m_BufferSize = CIRCULAR_BUFFER_SIZE;
            }

            kprintf("control 0x00 SampleRate: %d BitRate: %d Channels: %d buf size:%d\n",
                    m_SampleRate, m_BitRate, m_Channels, m_BufferSize);

            if (m_SampleRate == 8000 && m_Channels == 1) {

                short *buffer = NULL;
                signed char sessionNum;

                if (s_AudioBuffer.bufferStart) {
                    break;
                }

                zapi_Audio->Init(audio_event_callback,
                        AUDIO_SESSION_PLAY | AUDIO_FORMAT_PCM,
                        m_BufferSize, &buffer, &sessionNum);
                kprintf("after init buffer: 0x%08x session: %d\n",
                    buffer, sessionNum);

                s_AudioBuffer.isStarted = 0;

                s_AudioBuffer.isStopped = 1;

                s_AudioBuffer.writeOffset = 0;

                s_AudioBuffer.bufferSize = m_BufferSize;

                s_AudioBuffer.bufferStart = buffer;

                s_AudioBuffer.sessionNumber = sessionNum;

                s_AudioBuffer.wakeupEventPeriod = 1;

                if (m_AppPath == FIH_IIS_LOUDSPEAKER) {
                    m_AppPath = FIH_VOICE_MIC_AND_VOICE_LOUDSPEAKER;
                }
                else if (m_AppPath == FIH_IIS_HEADPHONE) {
                    m_AppPath = FIH_VOICE_HEADPHONE_MIC_AND_VOICE_HEADPHONE;
                }
                int res = zapi_Audio->Config(m_AppPath, m_volume);
                if (res) {
                    kprintf("change audio path failed!!!res=%d\n", res);
                    ec = E_FAIL;
                }
            }
            else {
                int res = zapi_Audio->Player_PowerOn(FIH_AUD_TYPE_MP3, FIH_MED_IIS);
                if (res > 0) {
                    kprintf("init audioic failed! res=%d\n", res);
                    //ec = E_FAIL;
                }
                zapi_Audio->Init48k(m_SampleRate, m_BitRate, m_Channels);

            }
            PoSetAudioPowerOn(TRUE);
            break;
        }

        case 0x01: {/* Master Volume */
            if (inDataSize < 1) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            int res = 0;
            m_volume = (*pInData) * FIH_MAX_VOLUME / MAX_VOLUME;
            kprintf("m_AppPath: %d, left: %d m_value: %d\n",
                m_AppPath, (int)(*pInData), m_volume);
            if (m_AppPath != FIH_ALL_PATH_CLOSE) {
                res = zapi_Audio->Config(m_AppPath, m_volume);
            }
            else if (m_AppPath == FIH_ALL_PATH_CLOSE &&
                    m_CurrentPath != FIH_ALL_PATH_CLOSE) {
                res = zapi_Audio->Config(m_CurrentPath, m_volume);
            }
            if (res) {
                kprintf("master audio volume errror! %d\n", res);
                ec = E_FAIL;
            }
            break;
        }

        case 0x02: {/* Voice Channel Volume(s) */
            if (inDataSize < 2) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            int res = 0;
            m_volume = MAX((int)(*pInData), (int)(*(pInData + 1))) *
                          FIH_MAX_VOLUME / MAX_VOLUME;
            kprintf("m_AppPath: %d, left: %d right: %d m_value: %d\n",
                m_AppPath, (int)(*pInData), (int)(*(pInData + 1)), m_volume);
            if (m_AppPath != FIH_ALL_PATH_CLOSE) {
                res = zapi_Audio->Config(m_AppPath, m_volume);
            }
            else if (m_AppPath == FIH_ALL_PATH_CLOSE &&
                    m_CurrentPath != FIH_ALL_PATH_CLOSE) {
                res = zapi_Audio->Config(m_CurrentPath, m_volume);
            }
            if (res) {
                kprintf("change audio volume errror! %d\n", res);
                ec = E_FAIL;
            }
            break;
        }
        case 0x10: {
            // Flush the buffers and close wave out device
            int res;
            EventState nState;

            if (m_SampleRate == 8000 && m_Channels == 1) {

                kprintf("control 10: app path= %d volume=%d buffer address=0x%08x\n",
                    m_AppPath, m_volume, s_AudioBuffer.bufferStart);

                if (s_AudioBuffer.bufferStart == 0) {
                    goto SHUTDOWN_ALL_DEVICE;
                }
    //            if (s_AudioBuffer.isStarted == 0 &&
    //                s_AudioBuffer.isStopped == 1) {
    //                goto SHUTDOWN_ALL_DEVICE;
    //            }

                if (s_AudioBuffer.isStarted == 0 &&
                    s_AudioBuffer.isStopped == 1) {
                    zapi_Audio->SetAppStatus(
                        s_AudioBuffer.sessionNumber, s_AudioBuffer.writeOffset);

                    zapi_Audio->StartSession(s_AudioBuffer.sessionNumber,
                        s_AudioBuffer.wakeupEventPeriod, 0, 0x1000, 0, 0x7fff);

                    s_AudioBuffer.isStarted = 1;

                    res = zapi_Audio->End(s_AudioBuffer.sessionNumber);
                    if (res != AUDIO_RESULT_SUCCESS) {
                        kprintf("wave out device end error! res: %d\n", res);
                        ec = E_FAIL;
                        goto SHUTDOWN_ALL_DEVICE;
                    }
                    m_AudioStopEvent.TryWait(DzMillisecondsToTicks(5000), &wr, &nState);
                    if (WaitResult_TimedOut == wr) {
                        kprintf("Wait audio stop timeout!!\n");
                        ec = E_TIMED_OUT;
                    }
                    res = zapi_Audio->Config(FIH_ALL_PATH_CLOSE, m_volume);
                    if (!res) {
                        m_AppPath = FIH_ALL_PATH_CLOSE;
                    }
                    s_AudioBuffer.isStopped = 1;
                    s_AudioBuffer.isStarted = 0;
                    s_AudioBuffer.writeOffset = 0;
                    s_AudioBuffer.bufferStart = 0;
                    goto SHUTDOWN_ALL_DEVICE;
                }

                res = zapi_Audio->Close(s_AudioBuffer.sessionNumber);
                if (res != AUDIO_RESULT_SUCCESS) {
                    kprintf("wave out device close error! res: %d\n", res);
                    ec = E_FAIL;
                    goto SHUTDOWN_ALL_DEVICE;
                }

                m_AudioStopEvent.TryWait(DzMillisecondsToTicks(5000), &wr, &nState);
                if (WaitResult_TimedOut == wr) {
                    kprintf("Wait audio stop timeout!!\n");
                    ec = E_TIMED_OUT;
                }
                res = zapi_Audio->Config(FIH_ALL_PATH_CLOSE, m_volume);
                if (!res) {
                    m_AppPath = FIH_ALL_PATH_CLOSE;
                }
                s_AudioBuffer.isStopped = 1;
                s_AudioBuffer.isStarted = 0;
                s_AudioBuffer.writeOffset = 0;
                s_AudioBuffer.bufferStart = 0;
            }
            else {
                zapi_Audio->End48k();
                zapi_Audio->Player_PowerOff();
                res = zapi_Audio->Config(FIH_ALL_PATH_CLOSE, m_volume);
                if (!res) {
                    m_AppPath = FIH_ALL_PATH_CLOSE;
                }
            }
SHUTDOWN_ALL_DEVICE:
            if (g_playThenAllDevicesDown) {
                g_playThenAllDevicesDown = 0;
            }
            PoSetAudioPowerOn(FALSE);
            break;
        }

        case 0x11:
            // Reset wave out device
            break;

        case 0x20: {
            // Open the speaker
            UInt32 device;
            int res;

            kprintf("control 20: app path=%d volume=%d\n",
                m_AppPath, m_volume);

            device = (0 == inDataSize) ?
            AODEVICE_SPEAKER : (UInt32)(*pInData);

            if (device == AODEVICE_SPEAKER) {
                kprintf("open speaker!\n");
                if (m_SampleRate == 8000 && m_Channels == 1) {
                    m_AppPath = FIH_VOICE_MIC_AND_VOICE_LOUDSPEAKER;
                }
                else {
                    m_AppPath = FIH_IIS_LOUDSPEAKER;
                }
            }
            else if (device == AODEVICE_HEADSET) {
                kprintf("open headset!\n");
                if (m_SampleRate == 8000 && m_Channels == 1) {
                    m_AppPath = FIH_VOICE_HEADPHONE_MIC_AND_VOICE_HEADPHONE;
                }
                else {
                    m_AppPath = FIH_IIS_HEADPHONE;
                }
            }
            else if (device == AODEVICE_BTHEADSET) {
                kprintf("open bt a2dp!\n");
                m_AppPath = FIH_STEREO_BT_A2DP;
                // Init a2dp
                if (!(m_SampleRate == 8000 && m_Channels == 1)) {
                    zapi_Audio->SetA2dpEnabled(1);
                }
            }
            else if (device == AODEVICE_HEADPHONE_RINGTONE) {
                kprintf("open ringtone!\n");
                m_AppPath = FIH_IIS_HEADPHONE_RINGTONE;
            }
            res = zapi_Audio->Config(m_AppPath, m_volume);
            if (res) {
                kprintf("init audio out errror! error code: %d\n", res);
                ec = E_FAIL;
            }

            kprintf("control 20: buffer address=0x%08x\n", s_AudioBuffer.bufferStart);
            break;
        }

        case 0x21: {
            // Close the speaker
            EventState nState;
            UInt32 device;

            kprintf("control 21: app path=%d volume=%d\n",
                m_AppPath, m_volume);

            if (m_SampleRate == 8000 && m_Channels == 1) {

                if (s_AudioBuffer.isStarted == 0) break;

                if (s_AudioBuffer.isStarted == 1 &&
                    s_AudioBuffer.isStopped == 1) {
                    uint_t sleepticks = DzMillisecondsToTicks(5000);
                    m_AudioStopEvent.TryWait(sleepticks, &wr, &nState);
                    if (WaitResult_TimedOut == wr) {
                        kprintf("audio wait timeout, maybe it has already been closed!\n");
                        ec = E_TIMED_OUT;
                        break;
                    }
                    zapi_Audio->Config(FIH_ALL_PATH_CLOSE, m_volume);
                    s_AudioBuffer.isStarted = 0;
                }
                else if (s_AudioBuffer.isStarted == 1 &&
                    s_AudioBuffer.isStopped == 0) {

                    device = (0 == inDataSize) ?
                    AODEVICE_SPEAKER : (UInt32)(*pInData);

                    if (device == AODEVICE_SPEAKER) {
                        if (m_AppPath != FIH_VOICE_MIC_AND_VOICE_LOUDSPEAKER)
                            break;
                    }
                    else if (device == AODEVICE_HEADSET) {
                        if (m_AppPath != FIH_VOICE_HEADPHONE_MIC_AND_VOICE_HEADPHONE)
                            break;
                    }
                    zapi_Audio->Config(m_AppPath, 0);
                }
                //kprintf("control 21: buffer address=0x%08x\n", s_AudioBuffer.bufferStart);
            }
            else {
                device = (0 == inDataSize) ?
                    AODEVICE_SPEAKER : (UInt32)(*pInData);

                if (device == AODEVICE_SPEAKER) {
                    if (m_AppPath != FIH_IIS_LOUDSPEAKER)
                        break;
                }
                else if (device == AODEVICE_HEADSET) {
                    if (m_AppPath != FIH_IIS_HEADPHONE)
                        break;
                }
                else if (device == AODEVICE_BTHEADSET) {
                    // end a2dp
                    zapi_Audio->SetA2dpEnabled(0);
                }
                zapi_Audio->Config(m_AppPath, 0);
                kprintf("control 21: close path %d\n", m_AppPath);
            }
            m_AppPath = FIH_ALL_PATH_CLOSE;
            break;
        }

        case 0x22:
            // Start vibration
            //
            s_vibratorEvent.Notify(VibratorStatus_Enable);
            break;

        case 0x23:
            // Stop vibration
            //
            s_vibratorEvent.Notify(VibratorStatus_Disable);
            break;
        case 0x24: {
            static Uint8 s_pArg[3];
            switch (s_touchVirationlevel) {
                case Vibrate_Disable: {
                    goto EXIT;
                }
                case Vibrate_Weak: {
                    s_pArg[0] = 2; //outputVoltage 1.4v
                    s_pArg[1] = 0; //cycle%100
                    s_pArg[2] = 7; //vibrate 7 ticks
                    break;
                }
                case Vibrate_Normal: {
                    s_pArg[0] = 2;
                    s_pArg[1] = 0;
                    s_pArg[2] = 12;
                    break;
                }
                case Vibrate_Strong: {
                    s_pArg[0] = 3;// 2.7v
                    s_pArg[1] = 0;
                    s_pArg[2] = 7;
                    break;
                }
                default:
                    assert(0);
            }
            zapi_Touch->vibration(s_pArg);
            break;
        }
        case 0x25: {//set touch vibration level
            if (inBuffer.GetCapacity() < 4) {
                ec = E_INVALID_ARGUMENT;
                break;
            }
            Int32 *plevel = (Int32 *)inBuffer.GetPayload();
            if (*plevel < Vibrate_Disable || *plevel > Vibrate_Strong) {
                ec = E_INVALID_ARGUMENT;
                break;
            }
            s_touchVirationlevel = *plevel;
            break;
        }
        ////////////////////////////////////////////////////////////////////////////
        //
        // Audiocard State 2: STATE_AUDIOIN
        //
        case 0x80: {
            // Recording params
            /*
             *  Sample rate & Channels
             *  Format: bit0-bit31 Sample Rate
                        bit32-bit47 Bytes Per Sample
                        bit48-bit63 Number of Channels(1 & 2)
             */
#ifdef RECORD_IN_ELASTOS
            short *buffer = NULL;
            signed char sessionNum;

            if (s_RecorderBuffer.bufferStart) {
                break;
            }

            zapi_Audio->Init(audio_event_callback,
                        AUDIO_SESSION_RECORD| AUDIO_FORMAT_PCM,
                        CIRCULAR_BUFFER_SIZE * 2, &buffer, &sessionNum);

            kprintf("Record: after init buffer=0x%08x session=%d\n",
                buffer, sessionNum);

            s_RecorderBuffer.bufferStart = buffer;

            s_RecorderBuffer.sessionNumber = sessionNum;

            s_RecorderBuffer.AppStatus = 0;

            s_RecorderBuffer.DSPStatus = 0;

            s_RecorderBuffer.unitByte = TRUE;

            s_RecorderBuffer.bufferSize = CIRCULAR_BUFFER_SIZE * 2;

            s_RecorderBuffer.wakeupEventPeriod = 1;

            zapi_Audio->StartSession(
                        s_RecorderBuffer.sessionNumber,
                        s_RecorderBuffer.wakeupEventPeriod,
                        0x1000, 0x1000, 0x7fff, 0x7fff);
#else
            zapi_Audio->Recorder_Init();
#endif

            break;
        }

        case 0x82: {
            // Set recording volume
            break;
        }

        case 0x83: {// Get recording volume
            break;
        }

        case 0x90: {
            //  Close audio in device
#ifdef RECORD_IN_ELASTOS
            EventState nState;
            int res = zapi_Audio->Close(
                                s_RecorderBuffer.sessionNumber);
            if (res != AUDIO_RESULT_SUCCESS) {
                kprintf("close recorder fail! res=%d\n", res);
                ec = E_FAIL;
            }

            //clear buffer
            int status = zapi_Audio->GetASFStatus(s_RecorderBuffer.sessionNumber);
            zapi_Audio->SetAppStatus(s_RecorderBuffer.sessionNumber, status);

            m_AudioStopEvent.TryWait(DzMillisecondsToTicks(5000), &wr, &nState);
            if (WaitResult_TimedOut == wr) {
                kprintf("Wait recorder stop timeout!!\n");
                ec = E_TIMED_OUT;
            }

            s_RecorderBuffer.bufferStart = 0;

            s_RecorderBuffer.sessionNumber = 0;

            s_RecorderBuffer.AppStatus = 0;

            s_RecorderBuffer.DSPStatus = 0;
#else
            zapi_Audio->Recorder_End();
#endif
            break;
        }

        case 0x91:
            // Reset audio in device
            break;

        case 0xa0:
            break;

        ////////////////////////////////////////////////////////////////////////////
        //
        // Audiocard State 3: STATE_CALL
        //
        case 0xb0: {
            UInt32 callMode;
//            int res = 0;

            callMode = (0 == inDataSize) ? CALL_NORMAL : (UInt32)(*pInData);
            switch (callMode) {
                case CALL_NORMAL:
                    m_AppPath = FIH_VOICE_MIC_AND_VOICE_RECEIVER;
                    break;
                case CALL_HANDFREE:
                    m_AppPath = FIH_VOICE_MIC_AND_VOICE_LOUDSPEAKER;
                    break;
                case CALL_HEADSET:
                    m_AppPath = FIH_VOICE_HEADPHONE_MIC_AND_VOICE_HEADPHONE;
                    break;
                case CALL_BLUETOOTH:
                    m_AppPath = FIH_VOICE_BT;
                    break;
                default:
                    break;
            }
//            if (s_AudioBuffer.isStarted == 1 &&
//                s_AudioBuffer.bufferStart != 0) {
//                kprintf("call connect when playing, close audio\n");
//                res = zapi_Audio->Close(s_AudioBuffer.sessionNumber);
//                if (res != AUDIO_RESULT_SUCCESS) {
//                    kprintf("in close wave out device audio close error! res: %d\n", res);
//                }
//                s_AudioBuffer.isStopped = 1;
//                s_AudioBuffer.bufferStart = 0;
//            }
            PoSetAudioPowerOn(TRUE);
            zapi_Audio->Config(m_AppPath, m_volume);
            break;
        }

        case 0xb1: {
            // Close current call
            zapi_Audio->Config(FIH_ALL_PATH_CLOSE, m_volume);
            m_AppPath = FIH_ALL_PATH_CLOSE;
            PoSetAudioPowerOn(FALSE);
            break;
        }

        case 0xb2: {
            // Set call volume
            int res = 0;
            if (inDataSize < 1) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            m_volume = (*pInData) * FIH_MAX_VOLUME / MAX_VOLUME;
            if (m_AppPath != FIH_ALL_PATH_CLOSE) {
                res = zapi_Audio->Config(m_AppPath, m_volume);
            }
            else if (m_AppPath == FIH_ALL_PATH_CLOSE &&
                    m_CurrentPath != FIH_ALL_PATH_CLOSE) {
                res = zapi_Audio->Config(m_CurrentPath, m_volume);
            }
            if (res) {
                kprintf("change audio volume errror! %d\n", res);
                ec = E_FAIL;
            }
            break;
        }
        ////////////////////////////////////////////////////////////////////////////
        //
        // Audiocard State Getting
        //
        case 0x100:
            // Get the driver state and capabilities
            break;
        case 0x101: {
            // Get the connected device
            UInt32 device;
            if (pOutBuffer->GetCapacity() < (int)sizeof(UInt32)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            device = (m_CurrentPath == FIH_VOICE_HEADPHONE_MIC_AND_VOICE_HEADPHONE) ? 1 : 0;
            pOutBuffer->Copy((Byte *)&device, sizeof(UInt32));
            break;
        }

    }

EXIT:
    m_lock.Unlock();
    return ec;
}

void CAudioCard::Dispose()
{
}

extern "C" void AudioHeadsetCallback(unsigned char event)
{
    switch(event) {
        case AUDIO_HEADSET_PLUGGED:
            //nu_puts("headset plugged!\n");
            nu_notifyDeviceCaller((Int32)&(s_audio.m_AudioHeadsetCallbackEvent), 0x30);
            break;
        case AUDIO_HEADSET_UNPLUGGED:
            //nu_puts("headset unplugged!\n");
            nu_notifyDeviceCaller((Int32)&(s_audio.m_AudioHeadsetCallbackEvent), 0x31);
            break;
        default:
            break;
    }
}

extern "C" void AudioHookCallback(unsigned char event)
{
    //char hookbuf[50];
	Int32 KeyNum;
    static Boolean firstPress = TRUE;
    Uint32 uFlags = 0;
	//sprintf(hookbuf, "hookevent = %d\n", event);
	//nu_puts(hookbuf);
	switch (event) {
	    case AUDIO_HEADSET_HOOK_DETECT_ON: {
	        if (firstPress) {
	            uFlags = SaveFlagsAndCli();
                KeyNum = g_pKeypad->m_keyRIndex - g_pKeypad->m_keyIndex;
                if ((1 != KeyNum) && -(BUFFERSIZE-1) != KeyNum) {
                    g_pKeypad->m_pNuKeyData[g_pKeypad->m_keyIndex].event_id = 1; //presee Status
                    g_pKeypad->m_pNuKeyData[g_pKeypad->m_keyIndex].keycode = NU_VK_LINECONTROL;
                    g_pKeypad->m_keyIndex++;

                    g_pKeypad->m_keyIndex = g_pKeypad->m_keyIndex % BUFFERSIZE;
                }
                firstPress = FALSE;
                RestoreIF(uFlags);
                //nu_puts("-------------------------NU_VK_LINECONTROL press\n");
            } else {
                //nu_puts("-------------------------NU_VK_LINECONTROL press\n");
                uFlags = SaveFlagsAndCli();
                KeyNum = g_pKeypad->m_keyRIndex - g_pKeypad->m_keyIndex;
                if ((1 != KeyNum) && -(BUFFERSIZE-1) != KeyNum) {
                    g_pKeypad->m_pNuKeyData[g_pKeypad->m_keyIndex].event_id = 0; //Release Status
                    g_pKeypad->m_pNuKeyData[g_pKeypad->m_keyIndex].keycode = NU_VK_LINECONTROL;
                    g_pKeypad->m_keyIndex++;
                    g_pKeypad->m_keyIndex = g_pKeypad->m_keyIndex % BUFFERSIZE;
                }


                //nu_puts("-------------------------NU_VK_LINECONTROL release\n");

                KeyNum = g_pKeypad->m_keyRIndex - g_pKeypad->m_keyIndex;
                if ((1 != KeyNum) && -(BUFFERSIZE-1) != KeyNum) {
                    g_pKeypad->m_pNuKeyData[g_pKeypad->m_keyIndex].event_id = 1; //presee Status
                    g_pKeypad->m_pNuKeyData[g_pKeypad->m_keyIndex].keycode = NU_VK_LINECONTROL;
                    g_pKeypad->m_keyIndex++;

                    g_pKeypad->m_keyIndex = g_pKeypad->m_keyIndex % BUFFERSIZE;
                }
                RestoreIF(uFlags);
	        }

	        nu_notifyDeviceCaller((Int32)(Int32)&(g_pKeypad->m_event), SIGNALED);
            break;
	    }
	    case AUDIO_HEADSET_HOOK_DETECT_OFF: {
	            uFlags = SaveFlagsAndCli();
                KeyNum = g_pKeypad->m_keyRIndex - g_pKeypad->m_keyIndex;
                if ((1 != KeyNum) && -(BUFFERSIZE-1) != KeyNum) {
                    g_pKeypad->m_pNuKeyData[g_pKeypad->m_keyIndex].event_id = 0; //Release Status
                    g_pKeypad->m_pNuKeyData[g_pKeypad->m_keyIndex].keycode = NU_VK_LINECONTROL;
                    g_pKeypad->m_keyIndex++;
                    g_pKeypad->m_keyIndex = g_pKeypad->m_keyIndex % BUFFERSIZE;
                }
                firstPress = TRUE;
                RestoreIF(uFlags);
                //nu_puts("-------------------------NU_VK_LINECONTROL release\n");
                nu_notifyDeviceCaller((Int32)(Int32)&(g_pKeypad->m_event), SIGNALED);

                break;
	    }
	    default :
	        break;
	}
}

ECode CAudioCard::AudioCallbackThread(void *arg)
{
    WaitResult wr;
    EventState nState;
    ECode ec = NOERROR;

    while (1) {
        m_AudioHeadsetCallbackEvent.Wait(&wr, &nState);
        m_lock.Lock(&wr);
        if (nState == 0x30) {
            m_CurrentPath = FIH_VOICE_HEADPHONE_MIC_AND_VOICE_HEADPHONE;
        }
        else if (nState == 0x31){
            m_CurrentPath = FIH_VOICE_MIC_AND_VOICE_LOUDSPEAKER;
        }

        EmIsrParameter Param;
        memset(&Param,0,sizeof(Param));

        if (m_CurrentPath == FIH_VOICE_HEADPHONE_MIC_AND_VOICE_HEADPHONE) {
            EmPostEventByIsr(120, &Param);
            kprintf("headset plugged!\n");
        }
        else if (m_CurrentPath == FIH_VOICE_MIC_AND_VOICE_LOUDSPEAKER){
            EmPostEventByIsr(121, &Param);
            kprintf("headset unplugged!\n");
        }
        m_lock.Unlock();
    }
    return ec;
}

ECode AudioThreadRoutine(void *arg)
{
    s_audio.AudioCallbackThread(arg);
    return NOERROR;
}

ECode CreateAudioThread()
{
    ECode ec;
    ec = DzCreateKernelThread(AudioThreadRoutine,
        (PVoid)NULL, CreateThreadFlag_System, ThreadPriorityRank_Normal, NULL);
    if (FAILED(ec)) {
        kprintf("unable to create Audio thread ec : %08x\n", ec);
        return ec;
    }
    return ec;
}

enum
{
    AUDIO_ACCESSORY_HEADSET = 0,
    AUDIO_ACCESSORY_HOOK = 1,
    AUDIO_ACCESSORY_MAX
};

EXTERN IDeviceDriver * CDECL CreateAudio(uint_t uDeviceNo, void *pvParameter)
{
    // Config vibrator
    //
    UInt8 ret = nu_ConfigVibrator(NULL, 1,
            3,  // outputVoltage, 3 means 2.7V
            0); // cycle, 0 means 100% duty cycle
    if (0 != ret) {
        return NULL;
    }

    ECode ec = DzCreateKernelThread(
        VibratorControlThread, NULL, CreateThreadFlag_System,
        ThreadPriorityRank_Normal, NULL);
    if (FAILED(ec)) {
        return NULL;
    }

    int res = zapi_Audio->RegisterCallback(
                        AudioHeadsetCallback,
                        AUDIO_ACCESSORY_HEADSET);
    if (0 != res) {
        return NULL;
    }
    res = zapi_Audio->RegisterCallback(
                        AudioHookCallback,
                        AUDIO_ACCESSORY_HOOK);
    if (0 != res) {
        return NULL;
    }
    CreateAudioThread();

    initbuffer16();

    return &s_audio;
}
